package com.yqiang.smscodedemo.service.impl;

import com.alibaba.fastjson2.JSON;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.yqiang.smscodedemo.domain.dto.SmsCodeSendReqDTO;
import com.yqiang.smscodedemo.service.ISmsCodeService;
import com.yqiang.smscodedemo.utils.AliyunSmsUtils;
import com.yqiang.smscodedemo.utils.IpUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * 短信验证码服务实现类
 */
@Service
@Slf4j
public class SmsCodeServiceImpl implements ISmsCodeService {
    public static final String VERIFY_CODE = "PHONE:CODE:VERIFY_CODE_%s_%s";
    public static final String IP_COUNT = "IP:CODE:COUNT:VERIFY_CODE_%s_%s";
    public static final int VERIFY_CODE_LENGTH = 6;
    public static final int VERIFY_CODE_EXPIRE_SECONDS = 300;
    public static final int IP_SEND_LIMIT_HOURS = 10;
    public static final int IP_SEND_LIMIT_COUNT = 10;
    public static final int SEND_INTERVAL_MILLIS = 60000;
    @Resource
    private AliyunSmsUtils aliyunSmsUtils;
    @Resource
    private RedisTemplate<String,String> redisTemplate;
    @Resource
    private HttpServletRequest request;
    @Override
    public void smsCodeSend(SmsCodeSendReqDTO smsCodeSendReqDTO) {
        String phone  = smsCodeSendReqDTO.getPhone();
        String businessType = smsCodeSendReqDTO.getBusinessType();

        // 参数校验
        if (!StringUtils.hasText(phone)) {
            throw new IllegalArgumentException("手机号不能为空");
        }
        if (!StringUtils.hasText(businessType)) {
            throw new IllegalArgumentException("业务类型不能为空");
        }

        //记录并判断一定时间内同一个ip发送验证码的次数
        String ipAddress = IpUtil.getIpAddress(request);
        String ipCountKey = String.format(IP_COUNT, ipAddress,businessType);
        Long sendCount = redisTemplate.opsForValue().increment(ipCountKey);
        if(sendCount == 1){
            redisTemplate.expire(ipCountKey,IP_SEND_LIMIT_HOURS,TimeUnit.HOURS);
        }else if(sendCount>IP_SEND_LIMIT_COUNT){
            throw new RuntimeException("同一IP地址10个小时只能发送验证码10次");
        }

        String verifyCodeKey = String.format(VERIFY_CODE, phone, businessType);
        /*判断验证码是否发送过，可用于从Redis拿去验证码，避免频繁;调用阿里云短信接口
        Boolean isKeyExists = redisTemplate.hasKey(verifyCodeKey);
        if(Boolean.TRUE.equals(isKeyExists)){
            //生成6位验证码
        }*/
        // 生成6位验证码
        int random = ThreadLocalRandom.current().nextInt(100000, 1000000);
        String verifyCode = String.valueOf(random);
        //发送验证码
        // 使用 SETNX 命令确保验证码在60秒内只能发送一次
        String tempKey = verifyCodeKey + ":temp";
        Boolean isSet = redisTemplate.opsForValue().setIfAbsent(tempKey, "1", SEND_INTERVAL_MILLIS, TimeUnit.MILLISECONDS);
        if (!Boolean.TRUE.equals(isSet)) {
            throw new RuntimeException("60秒之内不能重复发送验证码");
        }

        Map<String, String> params = new HashMap<>();
        params.put("code", verifyCode);
        String templateParam = JSON.toJSONString(params);
        log.info("向手机号{}发送验证码{}",phone,verifyCode);
        SendSmsResponse response = aliyunSmsUtils.sendSms(phone, templateParam);
        if (Objects.equals(HttpStatus.OK.value(),response.getStatusCode())) {
            //验证码发送成功
            redisTemplate.opsForValue().set(verifyCodeKey,verifyCode,300,TimeUnit.SECONDS);
        } else {
            // 删除临时键
            redisTemplate.delete(tempKey);
            throw new RuntimeException("验证码发送失败");
        }
    }

    @Override
    public boolean verify(String phone, String businessType, String verifyCode) {
        // 参数校验
        if (!StringUtils.hasText(phone)) {
            throw new IllegalArgumentException("手机号不能为空");
        }
        if (!StringUtils.hasText(businessType)) {
            throw new IllegalArgumentException("业务类型不能为空");
        }
        if (!StringUtils.hasText(verifyCode)) {
            throw new IllegalArgumentException("验证码不能为空");
        }
        String verifyCodeKey = String.format(VERIFY_CODE, phone, businessType);
        String verifyCodeValue = redisTemplate.opsForValue().get(verifyCodeKey);
        if (!StringUtils.hasText(verifyCodeValue)) {
            return false;
        }
        String[] split = verifyCodeValue.split("-");
        String code = split[0];
        return Objects.equals(verifyCode, code);
    }
}
